# _*_ coding: utf-8 _*_
from odoo import models, api, fields, _, SUPERUSER_ID
import odoo.addons.decimal_precision as dp
from odoo.exceptions import ValidationError, Warning


class PurchaseRequestOrder(models.Model):
    _inherit = ["order.priority.mixin", "stock.view.quant.mixin"]
    _name = "purchase.request.order"

    READONLY_STATES = {
        'purchase_requesting': [('readonly', True)],
        'price_comparing': [('readonly', True)],
        'allow_purchase': [('readonly', True)],
        'close': [('readonly', True)],
        'cancel': [('readonly', True)],
    }

    name = fields.Char('Order Reference', required=True, index=True, copy=False, default='New')
    date = fields.Date(string="Date", default=lambda self: fields.Date.today(), states=READONLY_STATES)
    data_planned = fields.Date(string="Scheduled Date", default=lambda self: fields.Date.today())
    supplier_id = fields.Many2one("res.partner", string="Supplier", required=True, states=READONLY_STATES)
    currency_id = fields.Many2one('res.currency', 'Currency', required=True, states=READONLY_STATES,
                                  default=lambda self: self.env.user.company_id.currency_id.id)
    line_ids = fields.One2many("purchase.request.order.line", "order_id", states=READONLY_STATES,
                               copy=True)

    amount_untaxed = fields.Monetary(string='Untaxed Amount', store=True, readonly=True, compute='_amount_all',
                                     track_visibility='always')
    amount_tax = fields.Monetary(string='Taxes', store=True, readonly=True, compute='_amount_all')
    amount_total = fields.Monetary(string='Total', store=True, readonly=True, compute='_amount_all')

    default_currency_id = fields.Many2one("res.currency", default=lambda self: self.env.user.company_id.currency_id,
                                          readonly=True)
    amount_untaxed_default_currency = fields.Monetary(currency_field="default_currency_id", compute='_amount_all',
                                                      string='Untaxed Amount Default Currency', store=True)
    amount_tax_default_currency = fields.Monetary(currency_field="default_currency_id", compute='_amount_all',
                                                  string='Taxes Default Currency', store=True)
    amount_total_default_currency = fields.Monetary(currency_field="default_currency_id", compute='_amount_all',
                                                    string='Total Default Currency', store=True)
    customers_total = fields.Monetary(currency_field="default_currency_id",
                                      compute='_amount_all',
                                      string='Total Customers Currency',
                                      store=True)
    freight_total = fields.Monetary(currency_field="default_currency_id",
                                    compute='_amount_all',
                                    string='Total Freight Currency',
                                    store=True)
    other_total = fields.Monetary(currency_field="default_currency_id",
                                  compute='_amount_all',
                                  string='Total Other Currency',
                                  store=True)
    quantity_total = fields.Float(string="Total Product Quantity", digits=dp.get_precision('Product Unit of Measure'), compute="_compute_quantity_total")

    department_id = fields.Many2one("hr.department", string="Department", states=READONLY_STATES)
    user_id = fields.Many2one("res.users", string="User", default=lambda self: self.env.user, states=READONLY_STATES)
    note = fields.Text(string="Note")
    company_id = fields.Many2one('res.company', 'Company', required=True, index=True, states=READONLY_STATES,
                                 default=lambda self: self.env.user.company_id.id)
    fiscal_position_id = fields.Many2one('account.fiscal.position', string='Fiscal Position', related="supplier_id.property_account_position_id", readonly=True)
    payment_term_id = fields.Many2one('account.payment.term', 'Payment Terms')
    vendor_payment_term_ids = fields.Many2many('account.payment.term', related='supplier_id.vendor_payment_term_ids', readonly=True)
    attachment_ids = fields.Many2many('ir.attachment', string='附件')

    state = fields.Selection([
        ('draft', 'Draft'),
        ("purchase_requesting", "Purchase Requesting"),
        ("price_comparing", "Price Comparing"),
        ("allow_purchase", "Allow Purchase"),
        ("close", "Close"),
        ("cancel", "Cancel"),
    ], string="State", default="draft", copy=False)
    origin = fields.Char(string='Source')
    incoterm = fields.Many2one('stock.incoterms', string='Incoterms')
    po_id = fields.Many2one('purchase.order', string='Purchase Order')
    picking_count = fields.Integer(compute='_compute_picking', string='Receptions', default=0)
    picking_ids = fields.Many2many('stock.picking', compute='_compute_picking', string='Receptions', copy=False)
    # fiscal_position_id = fields.Many2one('account.fiscal.position', string='Fiscal Position', oldname='fiscal_position')

    def _compute_quantity_total(self):
        for order in self:
            order.update({'quantity_total': sum(order.line_ids.mapped('product_qty'))})

    @api.depends('line_ids.price_total')
    def _amount_all(self):
        for order in self:
            amount_untaxed = amount_tax = 0.0
            customers_total = freight_total = other_total = 0.0
            for line in order.line_ids:
                amount_untaxed += line.price_subtotal
                customers_total += line.customers_amount
                freight_total += line.freight_price_default_currency
                other_total += line.other_price_default_currency
                # FORWARDPORT UP TO 10.0
                if order.company_id.tax_calculation_rounding_method == 'round_globally':
                    taxes = line.taxes_id.compute_all(line.price_unit, line.order_id.currency_id, line.product_qty, product=line.product_id, partner=line.order_id.partner_id)
                    amount_tax += sum(t.get('amount', 0.0) for t in taxes.get('taxes', []))
                else:
                    amount_tax += line.price_tax
            order.update({
                'amount_untaxed': order.currency_id.round(amount_untaxed),
                'amount_tax': order.currency_id.round(amount_tax),
                'customers_total': order.currency_id.round(customers_total),
                'freight_total': order.currency_id.round(freight_total),
                'other_total': order.currency_id.round(other_total),
                'amount_total': amount_untaxed + amount_tax,
            })
            default_currency = order.default_currency_id
            order.update({
                "amount_untaxed_default_currency": order.currency_id.compute(order["amount_untaxed"], default_currency),
                "amount_tax_default_currency": order.currency_id.compute(order["amount_tax"], default_currency),
                "amount_total_default_currency": order.currency_id.compute(order["amount_total"], default_currency) + customers_total + freight_total + other_total,
            })

    @api.model
    def create(self, vals):
        if vals.get('name', 'New') == 'New':
            vals['name'] = self.env['ir.sequence'].next_by_code('purchase.request.order') or '/'
        return super(PurchaseRequestOrder, self).create(vals)

    def action_confirm(self):
        for order in self:
            for line in order.line_ids:
                if not line.delivery_date:
                    raise ValidationError(_("Delivery Date not set in line."))
                # if line.price_unit != line.origin_price_unit and line.note is False:
                #     raise ValidationError(_("Changed price unit without log note."))
        self.write({
            "state": "purchase_requesting"
        })
        self.line_ids.write({
            "state": "purchase_requesting"
        })

    def action_approve(self):
        self.write({
            "state": "price_comparing"
        })
        self.line_ids.write({
            "state": "price_comparing"
        })
        for line in self.line_ids:
            sum_qty = sum(line.search([('requisition_line_id', '=', line.requisition_line_id.id)]).\
                          filtered(lambda r: r.state not in ('draft', 'purchase_requesting', 'cancel')).\
                          mapped('product_qty'))
            if sum_qty >= line.requisition_line_id.product_qty:
                line.requisition_line_id.write({'state': 'done'})

    @api.model
    def check_request_sum_qty_gt_requisition_qty(self):
        for pr_line in self.line_ids:
            # sum the product in all request's qty
            request_sum = sum(self.env['purchase.request.order.line'].
                              search([('requisition_line_id', '=', pr_line.requisition_line_id.id)]).
                              filtered(lambda p: p.state in ('already_purchase', 'close', 'allow_purchase')).
                              mapped('product_qty'))
            # check request sum qty gt requisition qty
            if request_sum + pr_line.product_qty > pr_line.requisition_line_id.product_qty:
                return True
            return False

    def action_allow_purchase(self):
        if self.check_request_sum_qty_gt_requisition_qty():
            raise Warning(_("Request qty can't greater than requisition qty"))
        self.write({
            "state": "allow_purchase"
        })
        self.line_ids.write({
            "state": "allow_purchase"
        })

    def action_cancel(self):
        self.write({
            "state": "cancel"
        })
        self.line_ids.write({
            "state": "cancel"
        })

    def action_close(self):
        self.write({
            "state": "close"
        })
        self.line_ids.write({
            "state": "close"
        })

    def action_create_purchase_order(self):
        self.ensure_one()
        order = self
        PurchaseOrder = self.env["purchase.order"]
        purchase_order = PurchaseOrder.search([
            ("partner_id", "=", order.supplier_id.id),
            ("user_id", "=", order.user_id.id),
            ("currency_id", "=", order.currency_id.id),
            ("payment_term_id", "=", order.payment_term_id.id),
            ("incoterm_id", "=", order.incoterm.id),
            ("state", "=", "draft")
        ])
        lines = order.line_ids.filtered(lambda line: line.state == "allow_purchase")

        if not lines:
            raise ValidationError(_("There is no allow purchase line."))

        line_vals = [(0, 0, {
            "name": l.product_id.name,
            "product_id": l.product_id.id,
            "product_uom": l.product_uom_id.id,
            "date_planned": l.delivery_date,
            "product_qty": l.product_qty,
            "price_unit": l.price_unit,
            "note": l.note,
            "requisition_memo": l.requisition_memo,
            "taxes_id": [(6, 0, l.taxes_id.ids)],
            "pr_line_id": l.id,
            "product_code": order._compute_product_code(l),
            "product_name": order._compute_product_name(l)
        }) for l in lines]
        if purchase_order:
            vals = {
                "order_line": line_vals
            }
            purchase_order[0].write(vals)
        else:
            vals = {
                "partner_id": order.supplier_id.id,
                "user_id": order.user_id.id,
                "payment_term_id": order.payment_term_id and order.payment_term_id.id or False,
                "pr_id": order.id,
                "order_line": line_vals,
                "currency_id": order.currency_id.id,
                "incoterm_id": order.incoterm and order.incoterm.id or False,
            }
            purchase_order = PurchaseOrder.create(vals)
        lines.write({
            "state": "already_purchase"
        })
        order.write({
            "po_id": purchase_order.id
        })
        pr_line = self.env['purchase.order.line'].search([('pr_line_id', '=', line_vals[0][2]['pr_line_id']), ('order_id', '=', purchase_order.id)])
        for line in lines:
            line.requisition_id.procurement_id.write({
                "purchase_line_id": pr_line.id
            })
        if purchase_order.origin and order.name not in purchase_order.origin:
            purchase_order.origin += "," + order.name
        else:
            purchase_order.origin = order.name

        return {
            'type': 'ir.actions.act_window',
            'res_model': purchase_order._name,
            'res_id': purchase_order.id,
            'view_type': 'form',
            'view_mode': 'form',
            'target': 'current'
        }

    @api.model
    def _compute_product_name(self, line):
        if not line.product_id:
            return ''

        seller = line.product_id._select_seller(
            partner_id=self.supplier_id,
            quantity=line.product_qty,
            date=fields.Date.today()[:10],
            uom_id=line.product_uom_id)

        if not seller:
            return ''

        return seller.product_name

    @api.model
    def _compute_product_code(self, line):
        if not line.product_id:
            return ''

        seller = line.product_id._select_seller(
            partner_id=self.supplier_id,
            quantity=line.product_qty,
            date=fields.Date.today()[:10],
            uom_id=line.product_uom_id)

        if not seller:
            return ''

        return seller.product_code

    @api.multi
    def action_view_quants(self):
        self.ensure_one()
        products = self.line_ids.mapped("product_id")
        return self.action_view_quant_by_product_ids(products.ids)

    @api.multi
    @api.onchange('supplier_id')
    def onchange_supplier_id(self):
        if not self.supplier_id:
            self.update({
                'payment_term_id': False,
            })
            return
        values = {
            'payment_term_id': self.supplier_id.property_supplier_payment_term_id and self.supplier_id.property_supplier_payment_term_id.id or False,
        }
        self.update(values)

    @api.depends('po_id.order_line.move_ids')
    def _compute_picking(self):
        for pr in self:
            order = pr.po_id
            pickings = self.env['stock.picking']
            for line in order.order_line:
                # We keep a limited scope on purpose. Ideally, we should also use move_orig_ids and
                # do some recursive search, but that could be prohibitive if not done correctly.
                moves = line.move_ids | line.move_ids.mapped('returned_move_ids')
                moves = moves.filtered(lambda r: r.state != 'cancel')
                pickings |= moves.mapped('picking_id')
            pr.picking_ids = pickings
            pr.picking_count = len(pickings)

    @api.multi
    def action_view_picking(self):
        '''
        This function returns an action that display existing picking orders of given purchase order ids.
        When only one found, show the picking immediately.
        '''
        action = self.env.ref('stock.action_picking_tree')
        result = action.read()[0]

        # override the context to get rid of the default filtering on picking type
        result['context'] = {}
        pick_ids = sum([order.picking_ids.ids for order in self], [])
        # choose the view_mode accordingly
        if len(pick_ids) > 1:
            result['domain'] = "[('id','in',[" + ','.join(map(str, pick_ids)) + "])]"
        elif len(pick_ids) == 1:
            res = self.env.ref('stock.view_picking_form', False)
            result['views'] = [(res and res.id or False, 'form')]
            result['res_id'] = pick_ids and pick_ids[0] or False
        return result

    @api.onchange('fiscal_position_id')
    def _compute_tax_id(self):
        """
        Trigger the recompute of the taxes if the fiscal position is changed on the PO.
        """
        for order in self:
            order.line_ids._compute_tax_id()

